{% from 'views/_helper.njk' import
  noteWrap
%}
{% from 'views/_parts.html' import
  supportEmail
%}

{% macro messagesLifespan(heading="#### 消息的有效期") -%}
{% if heading !== '' %}{{ heading }}{% endif %}

一个对话的消息记录会在云端保留 **6 个月**，也就是说一个对话可以查询到半年之内的历史消息记录。开发者可以付费来延长这一期限，请联系 {{ supportEmail() }}。{% if heading !== '' %}另外可以参考 [对话的有效期](#对话的有效期)。{% endif %}你也随时可以通过 REST API 将聊天记录同步到自己的服务器上。
{%- endmacro %}

{% macro conversationsLifespan(heading="#### 对话的有效期") -%}
{% if heading !== '' %}{{ heading }}{% endif %}

一个对话（包括普通、暂态、系统对话）如果 **1 年内** 没有通过 SDK 或者 REST API 发送过新的消息，或者它在 `_Conversation` 表中的任意字段没有被更新过，即被视为 **不活跃对话**，云端会自动将其删除。（查询对话的消息记录并不会更新 `_Conversation` 表，所以只查询不发送消息的对话仍会被视为不活跃对话。）

不活跃的对话被删除后，当客户端再次通过 SDK 或 REST API 对其发送消息时，会遇到 `4401 INVALID_MESSAGING_TARGET` 错误，表示该对话已经不存在了。同时，与该对话相关的消息历史也无法获取。

反之，活跃的对话会一直保存在云端。

{% if heading !== '' %}相关参考 [消息的有效期](#消息的有效期)。{% endif %}
{%- endmacro %}

{% macro clientOpenClose(methods={}) -%}
  {% if methods | length  %}
    {% call noteWrap() -%}
`{{methods.open}}` 这个方法表示开始连接 LeanCloud 云端服务器（即启动即时通讯服务），**它在整个使用周期内只需要调用一次**。用户退出即时通讯服务（断开 LeanCloud 云端服务器连接）时需要在 `IMClient` 对象上调用 `{{methods.close}}`。

这两个方法是成对使用的，在聊天过程中只需要调用一次，**无需多次调用**。
    {%- endcall %}
  {% endif %}
{%- endmacro %}

{% macro signature(heading="") -%}
  {% if heading != "" %}{{ heading }}{% endif %}

开发者可以在控制台中方便地测试签名。进入 {% if node == 'qcloud' %}**消息** > **即时通讯** > **用户**{% else %}[消息 > 即时通讯 > 用户](/dashboard/messaging.html?appid={{appid}}#/message/realtime/client){% endif %}，输入一个 `clientId` 进行查找，找到后界面会显示 **测试签名** 按键及更多内容。

<img src="images/console-im-signature-test.png" width="600" class="img-responsive" alt="一个标题为「测试签名」的表单，包含「签名类型」、「用户 ID」、「被操作者 ID」、「时间戳」和「随机字符串」输入框以及一个「生成签名」按钮。">
{%- endmacro %}

{% macro conversationProperties() %}
字段 | 说明
--- | ---
timestamp | 消息到达服务器的 Unix 时间戳（毫秒）
conv-id | 用于查询的对话 id
data | 消息内容，字符串形式的 JSON，格式请参考 [富媒体消息格式](realtime_rest_api.html#富媒体消息格式说明)。
from | 消息来自 id
msg-id | 消息 id
is-conv | 是否为 v2 中对话模型的消息
is-room | 是否为 v1 中对话模型的消息（为兼容而遗留下来的历史字段）
to | 消息发送目标，一般是目标会话的 id
bin | 消息是否为二进制编码，false 表示不是。
from-ip | 消息的来源 IP
ack-at | 消息送达到目标设备上的 Unix 时间戳（毫秒），但并不代表接收者已读该消息。如果消息未送达到设备，返回数据中**不会有该字段**。若消息有多个接收者，该值为送达到第一个接收者/目标设备上的时间。
{% endmacro %}

{% macro errorCodes(format='') %}
{% set items = [
  {
    code:     "1006",
    message:  "",
    desc:     "WebSocket 连接非正常关闭，通常见于路由器配置对长连接限制的情况。SDK 会自动重连，无需人工干预。"
  },
  {
    code: "4100",
    message: "APP_NOT_AVAILABLE",
    desc: "应用不存在或应用禁用了即时通讯服务。"
  },
  {
    code: "4101",
    message: "DUPLICATED_LOGIN",
    desc: "同一个设备重复登录推送服务。该错误码与即时通讯服务无关。"
  },
  {
    code: "4102",
    message: "SIGNATURE_FAILED",
    desc: "登录签名验证失败。"
  },
  {
    code: "4103",
    message: "INVALID_LOGIN",
    desc: "Client Id 格式错误，超过 64 个字符。"
  },
  {
    code: "4105",
    message: "SESSION_REQUIRED",
    desc: "Session 没有打开就发送消息，或执行其他操作。常见的错误场景是调用 open session 后直接发送消息，正确的用法是在 Session 打开的回调里执行。"
  },
  {
    code: "4107",
    message: "READ_TIMEOUT",
    desc: "读超时，云端长时间没有收到客户端的数据，切断连接。SDK 包装了心跳包的机制，出现此错误通常是网络问题。SDK 会自动重连，无需人工干预。"
  },
  {
    code: "4108",
    message: "LOGIN_TIMEOUT",
    desc: "登录超时，连接后长时间没有完成 session open。通常是登录被拒绝等原因，出现此问题可能是使用方式有误，可以 [创建工单](https://leanticket.cn/t/leancloud)，由我们技术顾问来给出建议。"
  },
  {
    code: "4109",
    message: "FRAME_TOO_LONG",
    desc: "包过长。消息大小超过 5 KB，请缩短消息或者拆分消息。"
  },
  {
    code: "4110",
    message: "INVALID_ORIGIN",
    desc: "设置安全域名后，当前登录的域名与安全域名不符合。"
  },
  {
    code: "4111",
    message: "SESSION_CONFLICT",
    desc: "设置单设备登录后，客户端被其他设备挤下线。"
  },
  {
    code: "4112",
    message: "SESSION_TOKEN_EXPIRED",
    desc: "Session 过期，请重新登录。"
  },
  {
    code: "4113",
    message: "APP_QUOTA_EXCEEDED",
    desc: "应用容量超限，当天登录用户数已经超过应用设定的最大值。"
  },
  {
    code: "4114",
    message: "UNPARSEABLE_RAW_MESSAGE",
    desc: "客户端发送的序列化数据服务器端无法解析。"
  },
  {
    code: "4115",
    message: "KICKED_BY_APP",
    desc: "客户端被 REST API 管理接口强制下线。"
  },
  {
    code: "4116",
    message: "MESSAGE_SENT_QUOTA_EXCEEDED",
    desc: "应用单位时间内发送消息量超过限制，消息被拒绝。"
  },
  {
    code: "4200",
    message: "INTERNAL_ERROR",
    desc: "服务器内部错误，如果反复出现请收集相关线索并 [创建工单](https://leanticket.cn/t/leancloud)，我们会尽快解决。"
  },
  {
    code: "4201",
    message: "SEND_MESSAGE_TIMEOUT",
    desc: "通过 API 发送消息超时。"
  },
  {
    code: "4301",
    message: "CONVERSATION_API_FAILED",
    desc: "上游 API 调用异常，请查看报错信息了解错误详情。"
  },
  {
    code: "4302",
    message: "CONVERSATION_SIGNATURE_FAILED",
    desc: "对话相关操作签名错误"
  },
  {
    code: "4303",
    message: "CONVERSATION_NOT_FOUND",
    desc: "发送消息、邀请加入对话等操作对应的对话不存在或因为 API 并发数超限等原因无法获取到对话信息。"
  },
  {
    code: "4304",
    message: "CONVERSATION_FULL",
    desc: "对话成员已满，不能再添加。"
  },
  {
    code: "4305",
    message: "CONVERSATION_REJECTED_BY_APP",
    desc: "对话操作被应用的云引擎 Hook 拒绝。"
  },
  {
    code: "4306",
    message: "CONVERSATION_UPDATE_FAILED",
    desc: "更新对话操作失败。"
  },
  {
    code: "4307",
    message: "CONVERSATION_READ_ONLY",
    desc: "该对话为只读，不能更新或增删成员。"
  },
  {
    code: "4308",
    message: "CONVERSATION_NOT_ALLOWED",
    desc: "该对话禁止当前用户发送消息。"
  },
  {
    code: "4309",
    message: "CONVERSATION_UPDATE_REJECTED",
    desc: "更新对话的请求被拒绝，当前用户不在对话中。"
  },
  {
    code: "4310",
    message: "CONVERSATION_QUERY_FAILED",
    desc: "查询对话失败，常见于慢查询导致的超时或受其他慢查询导致的数据库响应慢。"
  },
  {
    code: "4311",
    message: "CONVERSATION_LOG_FAILED",
    desc: "拉取对话消息记录失败，常见与超时的情况。"
  },
  {
    code: "4312",
    message: "CONVERSATION_LOG_REJECTED",
    desc: "拉取对话消息记录被拒绝，当前用户不在对话中。"
  },
  {
    code: "4313",
    message: "SYSTEM_CONVERSATION_REQUIRED",
    desc: "该功能仅对系统对话有效。"
  },
  {
    code: "4314",
    message: "NORMAL_CONVERSATION_REQUIRED",
    desc: "该功能仅对普通对话有效。"
  },
  {
    code: "4315",
    message: "CONVERSATION_TEMPORARY_BLACKLISTED",
    desc: "当前用户被加入此对话的临时黑名单，无法发送消息。"
  },
  {
    code: "4316",
    message: "TRANSIENT_CONVERSATION_REQUIRED",
    desc: "该功能仅对暂态对话有效。"
  },
  {
    code: "4317",
    message: "CONVERSATION_MEMBERSHIP_REQUIRED",
    desc: "该操作要求用户是对话成员。"
  },
  {
    code: "4318",
    message: "CONVERSATION_API_QUOTA_EXCEEDED",
    desc: "对话操作超出了 API 请求限制，需要提升应用级别。"
  },
  {
    code: "4401",
    message: "INVALID_MESSAGING_TARGET",
    desc: "发送消息的对话不存在，或当前用户不在对话中。"
  },
  {
    code: "4402",
    message: "MESSAGE_REJECTED_BY_APP",
    desc: "发送的消息被应用的云引擎 Hook 拒绝。"
  },
  {
    code: "4403",
    message: "MESSAGE_OWNERSHIP_REQUIRED",
    desc: "没有操作目标消息的权限"
  },
  {
    code: "4404",
    message: "MESSAGE_NOT_FOUND",
    desc: "找不到被操作的消息"
  }
] %}

{% if format == 'table' %}
代码|消息|说明
---|---|---
`0`|`(无)`|WebSocket 正常关闭，可能发生在服务器重启，或本地网络异常的情况。SDK 会自动重连，无需人工干预。{% for item in items %}
<code class="text-nowrap">{{item.code}}</code>|<code class="text-nowrap">{{ item.message if item.message else "(无)" | escape }}</code>|{{ item.desc | escape }}{% endfor %}
{% else %}
  {% for item in items %}

  ## {{ item.code }}

  - 信息 - <code>{{ item.message if item.message else "(无)" | escape }}</code>
  - 含义 - {{ item.desc | escape }}
  - 模块 - 即时通讯 IM
  {% endfor %}
{% endif %}
{% endmacro %}
